﻿
namespace SistemaLogistica.DataAccess
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.Common;
    using SistemaLogistica.Business.Entities;

    public class FacturaData : BaseDataAccess
    {
        private FacturaData() { }

        private readonly static FacturaData instancia = new FacturaData();

        public static FacturaData Instancia
        {
            get { return instancia; }
        }

        public List<Factura> Listar()
        {
            List<Factura> facturas = new List<Factura>();

            DbCommand command = db.GetStoredProcCommand("GetAllFromFactura");
            using (IDataReader dr = db.ExecuteReader(command))
            {
                while (dr.Read())
                {
                    Factura factura = new Factura
                    {
                        IdFactura = dr.GetInt32(dr.GetOrdinal("IdFactura")),
                        Descripcion = dr.GetString(dr.GetOrdinal("Descripcion")),
                        Numero = dr.GetString(dr.GetOrdinal("Numero")),
                        Fecha = dr.GetDateTime(dr.GetOrdinal("Fecha")),
                        Serie = dr.GetString(dr.GetOrdinal("Serie")),
                        Empleados = EmpleadoData.Instancia.ObtenerPorID(dr.GetInt16(dr.GetOrdinal("IDEmpleado"))),
                        OrdenCompra = OrdenCompraData.Instancia.ObtenerPorID(dr.GetInt32(dr.GetOrdinal("IDOrdenCompra"))),
                        Detalle = DetalleFacturaData.Instancia.ListarPorFactura(dr.GetInt32(dr.GetOrdinal("IdFactura")))
                    };
                    facturas.Add(factura);
                }
            }
            return facturas;
        }

        public int Registrar(Factura factura)
        {
            using (DbConnection conection = db.CreateConnection())
            {
                conection.Open();
                using (DbTransaction transaction = conection.BeginTransaction())
                {
                    try
                    {
                        #region 1.- Registrar Nota Ingreso

                        DbCommand dbcIngreso = db.GetStoredProcCommand("InsertFactura");

                        db.AddOutParameter(dbcIngreso, "IdFactura", DbType.Int32, 10);
                        db.AddInParameter(dbcIngreso, "fecha", DbType.DateTime, factura.Fecha);
                        db.AddInParameter(dbcIngreso, "IdEmpleado", DbType.Int32, factura.Empleados.IdEmpleado);
                        db.AddInParameter(dbcIngreso, "IdOrdenCompra", DbType.Int32, factura.OrdenCompra.IdOrdenCompra);
                        db.AddInParameter(dbcIngreso, "descripcion", DbType.String, factura.Descripcion);
                        db.AddInParameter(dbcIngreso, "serie", DbType.String, factura.Serie);
                        db.AddInParameter(dbcIngreso, "numero", DbType.String, factura.Numero);
                        db.ExecuteNonQuery(dbcIngreso, transaction);
                        factura.IdFactura = Convert.ToInt32(db.GetParameterValue(dbcIngreso, "IdFactura"));

                        #endregion
                        #region 2.- Registrar Detalles del Ingreso

                        DbCommand dbcDetalle = db.GetStoredProcCommand("InsertDetalleFactura");

                        db.AddInParameter(dbcDetalle, "cantidad", DbType.Decimal);
                        db.AddInParameter(dbcDetalle, "IdFactura", DbType.Int32);
                        db.AddInParameter(dbcDetalle, "precio", DbType.Currency);
                        db.AddInParameter(dbcDetalle, "IdBien", DbType.Int32);


                        DbCommand dbcProducto = db.GetStoredProcCommand("InsertDatosProducto");
                        db.AddInParameter(dbcProducto, "IdBien", DbType.Int32);
                        db.AddInParameter(dbcProducto, "stockActual", DbType.Decimal);
                        db.AddInParameter(dbcProducto, "Precio", DbType.Currency);


                        foreach (DetalleFactura detalle in factura.Detalle)
                        {
                            #region 2.1.- Verificar la existencia de Productos
                            // Si existe: Actualizar Cantidad
                            // No existe: Registrar Producto

                            db.SetParameterValue(dbcProducto, "IdBien", detalle.Bien.IdBien);
                            db.SetParameterValue(dbcProducto, "stockActual", detalle.Cantidad);
                            db.SetParameterValue(dbcProducto, "Precio", detalle.Precio);
                            db.ExecuteNonQuery(dbcProducto, transaction);
                            #endregion

                            #region 2.2.- Registrar Detalle

                            db.SetParameterValue(dbcDetalle, "cantidad", detalle.Cantidad);
                            db.SetParameterValue(dbcDetalle, "IdFactura", factura.IdFactura);
                            db.SetParameterValue(dbcDetalle, "precio", detalle.Precio);
                            db.SetParameterValue(dbcDetalle, "IdBien", detalle.Bien.IdBien);
                            db.ExecuteNonQuery(dbcDetalle, transaction);

                            #endregion
                        }

                        #endregion
                        #region 3.-Actualizar Estado OrdenCompra

                        DbCommand dbcOrdenCompra = db.GetStoredProcCommand("UpdateEstadoOrdenPedido");
                        db.AddInParameter(dbcOrdenCompra, "IdOrdenCompra", DbType.Int32, factura.OrdenCompra.IdOrdenCompra);
                        db.AddInParameter(dbcOrdenCompra, "estado", DbType.Boolean, true);
                        db.ExecuteNonQuery(dbcOrdenCompra, transaction);

                        #endregion

                        transaction.Commit();
                    }
                    catch (Exception ex)
                    {
                        transaction.Rollback();
                        throw ex;
                    }
                }
            }

            return factura.IdFactura;
        }

        public void Modificar(Factura factura)
        {
            using (DbConnection conection = db.CreateConnection())
            {
                conection.Open();
                using (DbTransaction transaction = conection.BeginTransaction())
                {
                    try
                    {
                        #region 1.- Retornar a su estado Inicial

                        #region 1.1.- Recoger Informacion Antigua

                        List<DetalleFactura> detalle = DetalleFacturaData.Instancia.ListarPorFactura(factura.IdFactura);

                        #endregion
                        #region 1.2.- Restar Cantidad de Bien

                        DbCommand dbcBienEdit = db.GetStoredProcCommand("InsertDatosProducto");
                        db.AddInParameter(dbcBienEdit, "IdBien", DbType.Int32);
                        db.AddInParameter(dbcBienEdit, "stockActual", DbType.Decimal);
                        db.AddInParameter(dbcBienEdit, "precio", DbType.Currency);


                        foreach (DetalleFactura item in detalle)
                        {
                            db.SetParameterValue(dbcBienEdit, "stockActual", -item.Cantidad);
                            db.SetParameterValue(dbcBienEdit, "IdBien", item.Bien.IdBien);
                            db.SetParameterValue(dbcBienEdit, "precio", item.Precio);

                            db.ExecuteNonQuery(dbcBienEdit, transaction);
                        }

                        #endregion
                        #region 1.3.- Eliminar Detalle Factura

                        DbCommand dbcDetalleEdit = db.GetStoredProcCommand("DeleteDetalleFactura");

                        db.AddInParameter(dbcDetalleEdit, "IdFactura", DbType.Int32);
                        db.AddInParameter(dbcDetalleEdit, "IdBien", DbType.Int32);

                        foreach (DetalleFactura item in detalle)
                        {
                            db.SetParameterValue(dbcDetalleEdit, "IdFactura", factura.IdFactura);
                            db.SetParameterValue(dbcDetalleEdit, "IdBien", item.Bien.IdBien);
                            db.ExecuteNonQuery(dbcDetalleEdit, transaction);
                        }

                        #endregion

                        #endregion
                        #region 2.- Ingresar la Nueva Informacion

                        #region 2.1.- Registrar Nota Ingreso

                        DbCommand dbcIngreso = db.GetStoredProcCommand("UpdateFactura");

                        db.AddInParameter(dbcIngreso, "IdFactura", DbType.Int32, factura.IdFactura);
                        db.AddInParameter(dbcIngreso, "fecha", DbType.DateTime, factura.Fecha);
                        db.AddInParameter(dbcIngreso, "IdEmpleado", DbType.Int32, factura.Empleados.IdEmpleado);
                        db.AddInParameter(dbcIngreso, "IdOrdenCompra", DbType.Int32, factura.OrdenCompra.IdOrdenCompra);
                        db.AddInParameter(dbcIngreso, "descripcion", DbType.String, factura.Descripcion);
                        db.AddInParameter(dbcIngreso, "serie", DbType.String, factura.Serie);
                        db.AddInParameter(dbcIngreso, "numero", DbType.String, factura.Numero);
                        db.ExecuteNonQuery(dbcIngreso, transaction);

                        #endregion
                        #region 2.2.- Registrar Detalles del Ingreso

                        DbCommand dbcDetalle = db.GetStoredProcCommand("InsertDetalleFactura");

                        db.AddInParameter(dbcDetalle, "cantidad", DbType.Decimal);
                        db.AddInParameter(dbcDetalle, "IdFactura", DbType.Int32);
                        db.AddInParameter(dbcDetalle, "precio", DbType.Currency);
                        db.AddInParameter(dbcDetalle, "IdBien", DbType.Int32);


                        DbCommand dbcProducto = db.GetStoredProcCommand("InsertDatosProducto");
                        db.AddInParameter(dbcProducto, "IdBien", DbType.Int32);
                        db.AddInParameter(dbcProducto, "stockActual", DbType.Decimal);
                        db.AddInParameter(dbcProducto, "Precio", DbType.Currency);


                        foreach (DetalleFactura item in factura.Detalle)
                        {
                            #region 2.2.1.- Verificar la existencia de Productos
                            // Si existe: Actualizar Cantidad
                            // No existe: Registrar Producto

                            db.SetParameterValue(dbcProducto, "IdBien", item.Bien.IdBien);
                            db.SetParameterValue(dbcProducto, "stockActual", item.Cantidad);
                            db.SetParameterValue(dbcProducto, "Precio", item.Precio);
                            db.ExecuteNonQuery(dbcProducto, transaction);


                            #endregion

                            #region 2.2.2.- Registrar Detalle

                            db.SetParameterValue(dbcDetalle, "cantidad", item.Cantidad);
                            db.SetParameterValue(dbcDetalle, "IdFactura", factura.IdFactura);
                            db.SetParameterValue(dbcDetalle, "precio", item.Precio);
                            db.SetParameterValue(dbcDetalle, "IdBien", item.Bien.IdBien);
                            db.ExecuteNonQuery(dbcDetalle, transaction);
                            #endregion
                        }

                        #endregion

                        #endregion
                        transaction.Commit();
                    }
                    catch (Exception ex)
                    {
                        transaction.Rollback();
                        throw ex;
                    }
                }
            }

        }

        public void Eliminar(int idFactura)
        {
            using (DbConnection conection = db.CreateConnection())
            {
                conection.Open();
                using (DbTransaction transaction = conection.BeginTransaction())
                {
                    try
                    {
                        #region 1.- Retornar a su estado Inicial

                        #region 1.1.- Recoger Informacion Antigua

                        List<DetalleFactura> detalle =DetalleFacturaData.Instancia.ListarPorFactura(idFactura);

                        #endregion
                        #region 1.2.- Restar Cantidad de Bien

                        DbCommand dbcBienEdit = db.GetStoredProcCommand("InsertDatosProducto");
                        db.AddInParameter(dbcBienEdit, "IdBien", DbType.Int32);
                        db.AddInParameter(dbcBienEdit, "stockActual", DbType.Decimal);
                        db.AddInParameter(dbcBienEdit, "precio", DbType.Currency);


                        foreach (DetalleFactura item in detalle)
                        {
                            db.SetParameterValue(dbcBienEdit, "stockActual", -item.Cantidad);
                            db.SetParameterValue(dbcBienEdit, "IdBien", item.Bien.IdBien);
                            db.SetParameterValue(dbcBienEdit, "precio", item.Precio);

                            db.ExecuteNonQuery(dbcBienEdit, transaction);
                        }

                        #endregion

                        #endregion
                        #region 2.- Eliminar Factura

                        DbCommand dbcIngreso = db.GetStoredProcCommand("DeleteFactura");

                        db.AddInParameter(dbcIngreso, "@IdFactura", DbType.Int32, idFactura);
                        db.ExecuteNonQuery(dbcIngreso);

                        #endregion

                        transaction.Commit();
                    }
                    catch (Exception ex)
                    {
                        transaction.Rollback();
                        throw ex;
                    }
                }
            }
        }

        public List<Factura> BuscarPorProveedor(int idproveedor)
        {
            List<Factura> facturas = new List<Factura>();
            List<DetalleFactura> listaDetalleFactura = new List<DetalleFactura>();

            DbCommand command = db.GetStoredProcCommand("GetFacturaByProveedorID");
            db.AddInParameter(command, "idProveedor", DbType.Int32, idproveedor);

            using (IDataReader dr = db.ExecuteReader(command))
            {
                while (dr.Read())
                {
                    Factura factura = new Factura
                    {
                        IdFactura = dr.GetInt32(dr.GetOrdinal("IdFactura")),
                        Descripcion = dr.GetString(dr.GetOrdinal("Descripcion")),
                        Numero = dr.GetString(dr.GetOrdinal("Numero")),
                        Fecha = dr.GetDateTime(dr.GetOrdinal("Fecha")),
                        Serie = dr.GetString(dr.GetOrdinal("Serie")),
                        Empleados = EmpleadoData.Instancia.ObtenerPorID(dr.GetInt16(dr.GetOrdinal("IDEmpleado"))),
                        OrdenCompra = OrdenCompraData.Instancia.ObtenerPorID(dr.GetInt32(dr.GetOrdinal("IDOrdenCompra"))),
                        Detalle = DetalleFacturaData.Instancia.ListarPorFactura(dr.GetInt32(dr.GetOrdinal("IdFactura")))
                    };
                    facturas.Add(factura);
                }
            }
            return facturas;
        }

        public List<string> ObtenerSeries(int idProveedor)
        {
            List<string> series = new List<string>();

            DbCommand command = db.GetStoredProcCommand("GetSerieFacturaByProveedorID");
            db.AddInParameter(command, "idProveedor", DbType.Int32, idProveedor);

            using (IDataReader dr = db.ExecuteReader(command))
            {
                while (dr.Read())
                {
                    series.Add(dr.GetString(dr.GetOrdinal("Serie")));
                }
            }
            return series;
        }

        public List<string> ObtenerNumerosDocumentos(int idProveedor, string serie)
        {
            List<string> numeros = new List<string>();

            DbCommand command = db.GetStoredProcCommand("GetNumeroFacturaByProveedorIDAndSerie");
            db.AddInParameter(command, "idProveedor", DbType.Int32, idProveedor);
            db.AddInParameter(command, "serie", DbType.String, serie);

            using (IDataReader dr = db.ExecuteReader(command))
            {
                while (dr.Read())
                {
                    numeros.Add(dr.GetString(dr.GetOrdinal("Numero")));
                }
            }
            return numeros;
        }

        public Factura ObtenerFactura(int idProveedor, string serie, string numero)
        {
            Factura factura = null;
            DbCommand command = db.GetStoredProcCommand("GetFacturaByProveedorIDAndSerieAndNumero");
            db.AddInParameter(command, "idProveedor", DbType.Int32, idProveedor);
            db.AddInParameter(command, "serie", DbType.String, serie);
            db.AddInParameter(command, "numero", DbType.String, numero);

            using (IDataReader dr = db.ExecuteReader(command))
            {
                if (dr.Read())
                {
                    factura = new Factura
                    {
                        IdFactura = dr.GetInt32(dr.GetOrdinal("IdFactura")),
                        Descripcion = dr.GetString(dr.GetOrdinal("Descripcion")),
                        Numero = dr.GetString(dr.GetOrdinal("Numero")),
                        Fecha = dr.GetDateTime(dr.GetOrdinal("Fecha")),
                        Serie = dr.GetString(dr.GetOrdinal("Serie")),
                        Empleados = EmpleadoData.Instancia.ObtenerPorID(dr.GetInt16(dr.GetOrdinal("IDEmpleado"))),
                        OrdenCompra = OrdenCompraData.Instancia.ObtenerPorID(dr.GetInt32(dr.GetOrdinal("IDOrdenCompra"))),
                        Detalle = DetalleFacturaData.Instancia.ListarPorFactura(dr.GetInt32(dr.GetOrdinal("IdFactura")))
                    };
                }
            }
            return factura;
        }

        public Factura ObtenerPorID(int idFactura)
        {
            Factura factura = null;
            DbCommand command = db.GetStoredProcCommand("GetFacturaByIdFactura");
            db.AddInParameter(command, "idFactura", DbType.Int32, idFactura);
            using (IDataReader dr = db.ExecuteReader(command))
            {
                if (dr.Read())
                {
                    factura = new Factura
                    {
                        IdFactura = dr.GetInt32(dr.GetOrdinal("IdFactura")),
                        Descripcion = dr.GetString(dr.GetOrdinal("Descripcion")),
                        Numero = dr.GetString(dr.GetOrdinal("Numero")),
                        Fecha = dr.GetDateTime(dr.GetOrdinal("Fecha")),
                        Serie = dr.GetString(dr.GetOrdinal("Serie")),
                        Empleados = EmpleadoData.Instancia.ObtenerPorID(dr.GetInt16(dr.GetOrdinal("IDEmpleado"))),
                        OrdenCompra = OrdenCompraData.Instancia.ObtenerPorID(dr.GetInt32(dr.GetOrdinal("IDOrdenCompra"))),
                        Detalle = DetalleFacturaData.Instancia.ListarPorFactura(dr.GetInt32(dr.GetOrdinal("IdFactura")))
                    };
                }
            }
            return factura;
        }
    }
}
